Crate palette

source ·
Expand description

A library that makes linear color calculations and conversion easy and accessible for anyone. It uses the type system to enforce correctness and to avoid mistakes, such as mixing incompatible color types.

§Where Do I Start?

The sections below give an overview of how the types in this library work, including color conversion. If you want to get your hands dirty, you’ll probably want to start with Srgb or Srgba. They are aliases for the more generic Rgb type and represent sRGB(A), the most common RGB format in images and tools. Their documentation has more details and examples.

The documentation for each module and type goes deeper into their concepts. Here are a few you may want to read:

  • Rgb - For getting started with RGB values.
  • Alpha - For more details on transparency.
  • convert - Describes the conversion traits and how to use and implement them.
  • cast - Describes how to cast color types to and from other data formats, such as arrays and unsigned integers.
  • color_difference - Describes different ways of measuring the difference between colors.

§Type Safety for Colors

Digital colors are not “just RGB”, and not even RGB is “just RGB”. There are multiple representations of color, with a variety of pros and cons, and multiple standards for how to encode and decode them. Palette represents these “color spaces” as separate types for increased expressiveness and to prevent mistakes.

Taking RGB as an example, it’s often stored or displayed as “gamma corrected” values, meaning that a non-linear function has been applied to its values. This encoding is not suitable for all kinds of calculations (such as rescaling) and will give visibly incorrect results. Functions that require linear RGB can therefore request, for example, LinSrgb as their input type.

// Srgb is an alias for Rgb<Srgb, T>, which is what most pictures store.
// LinSrgb is an alias for Rgb<Linear<Srgb>, T>, better for color manipulation.
use palette::{Srgb, LinSrgb};

fn do_something(a: LinSrgb, b: LinSrgb) -> LinSrgb {
// ...
}

let orangeish = Srgb::new(1.0, 0.6, 0.0);
let blueish = Srgb::new(0.0, 0.2, 1.0);
let result = do_something(orangeish, blueish); // Does not compile

The colors will have to be decoded before being used in the function:

// Srgb is an alias for Rgb<Srgb, T>, which is what most pictures store.
// LinSrgb is an alias for Rgb<Linear<Srgb>, T>, better for color manipulation.
use palette::{Srgb, LinSrgb};

fn do_something(a: LinSrgb, b: LinSrgb) -> LinSrgb {
// ...
}

let orangeish = Srgb::new(1.0, 0.6, 0.0).into_linear();
let blueish = Srgb::new(0.0, 0.2, 1.0).into_linear();
let result = do_something(orangeish, blueish);

See the rgb module for a deeper dive into RGB and (non-)linearity.

§Color Spaces and Conversion

As the previous section mentions, there are many different ways of representing colors. These “color spaces” are represented as different types in Palette, each with a description of what it is and how it works. Most of them also have two type parameters for customization:

  • The component type (T) that decides which number type is used. The default is f32, but u8, f64, and any other type that implement the required traits will work. Including SIMD types in many cases.
  • The reference white point (W) or standard (S) that affects the range, encoding or display properties of the color. This varies between color spaces and can usually be left as its default or be set via a type alias. For example, the Srgb and LinSrgb type aliases are both variants of the Rgb type, but with different standard (S) types.

Selecting the proper color space can have a big impact on how the resulting image looks (as illustrated by some of the programs in examples), and Palette makes the conversion between them as easy as a call to from_color or into_color.

This example takes an sRGB color, converts it to CIE L*C*h°, a color space similar to the colloquial HSL/HSV color spaces, shifts its hue by 180° and converts it back to RGB:

use palette::{FromColor, ShiftHue, IntoColor, Lch, Srgb};

let lch_color: Lch = Srgb::new(0.8, 0.2, 0.1).into_color();
let new_color = Srgb::from_color(lch_color.shift_hue(180.0));

§Transparency

There are many cases where pixel transparency is important, but there are also many cases where it would just be unused memory space. Palette has therefore adopted a structure where the transparency component (alpha) is attachable using the Alpha type. This approach has shown to be very modular and easy to maintain, compared to having transparent copies of each type.

An additional benefit is allowing operations to selectively affect the alpha component:

// Each color type has a transparent alias that ends with "a" for "alpha"
use palette::{LinSrgb, LinSrgba};

let mut c1 = LinSrgba::new(1.0, 0.5, 0.5, 0.8);
let c2 = LinSrgb::new(0.5, 1.0, 1.0);

c1.color = c1.color * c2; //Leave the alpha as it is
c1.blue += 0.2; //The color components can easily be accessed
c1 = c1 * 0.5; //Scale both the color and the alpha

There’s also PreAlpha that represents pre-multiplied alpha (also known as alpha masked colors). It’s commonly used in color blending and composition.

§Images and Buffers

Oftentimes, pixel data is stored in a plain array or slice such as a [u8; 3]. The cast module allows for easy conversion between Palette colors and arrays or slices. This also helps when working with other crates or systems. Here’s an example of how the pixels in an image from the image crate can be worked with as Srgb<u8>:

use image::RgbImage;
use palette::{Srgb, Oklab, cast::FromComponents, Lighten, IntoColor, FromColor};

fn lighten(image: &mut RgbImage, amount: f32) {
    // RgbImage can be dereferenced as [u8], allowing us to cast it as a
    // component slice to sRGB with u8 components.
    for pixel in <&mut [Srgb<u8>]>::from_components(&mut **image) {
        // Converting to linear sRGB with f32 components, and then to Oklab.
        let color: Oklab = pixel.into_linear::<f32>().into_color();

        let lightened_color = color.lighten(amount);

        // Converting back to non-linear sRGB with u8 components.
        *pixel = Srgb::from_linear(lightened_color.into_color());
    }
}

Some of the conversions are also implemented on the color types as From, TryFrom, Into, TryFrom and AsRef. This example shows how from can be used to convert a [u8;3] into a Palette color, into_format converts from Srgb<u8> to Srgb<f32>, and finally into converts back from a Palette color back to a [u8;3]:

use approx::assert_relative_eq;
use palette::Srgb;

let buffer = [255, 0, 255];
let srgb = Srgb::from(buffer);
assert_eq!(srgb, Srgb::<u8>::new(255u8, 0, 255));

let srgb_float: Srgb<f32> = srgb.into_format();
assert_relative_eq!(srgb_float, Srgb::new(1.0, 0.0, 1.0));

let array: [u8; 3] = srgb_float.into_format().into();
assert_eq!(array, buffer);

§A Basic Workflow

The overall workflow can be divided into three steps, where the first and last may be taken care of by other parts of the application:

Decoding -> Processing -> Encoding

§1. Decoding

Find out what the source format is and convert it to a linear color space. There may be a specification, such as when working with SVG or CSS.

When working with RGB or gray scale (luma):

  • If you are asking your user to enter an RGB value, you are in a gray zone where it depends on the context. It’s usually safe to assume sRGB, but sometimes it’s already linear.

  • If you are decoding an image, there may be some meta data that gives you the necessary details. Otherwise it’s most commonly sRGB. Usually you will end up with a slice or vector with RGB bytes, which can easily be converted to Palette colors:

use palette::{Srgb, cast::ComponentsAsMut};

// This works for any color type (not only RGB) that can have the
// buffer element type as component.
let color_buffer: &mut [Srgb<u8>] = image_buffer.components_as_mut();
  • If you are getting your colors from the GPU, in a game or other graphical application, or if they are otherwise generated by the application, then chances are that they are already linear. Still, make sure to check that they are not being encoded somewhere.

When working with other colors:

  • For HSL, HSV, HWB: Check if they are based on any other color space than sRGB, such as Adobe or Apple RGB.

  • For any of the CIE color spaces, check for a specification of white point and light source. These are necessary for converting to RGB and other colors, that depend on perception and “viewing devices”. Common defaults are the D65 light source and the sRGB white point. The Palette defaults should take you far.

§2. Processing

When your color has been decoded into some Palette type, it’s ready for processing. This includes things like blending, hue shifting, darkening and conversion to other formats. Just make sure that your non-linear RGB is made linear first (my_srgb.into_linear()), to make the operations available.

Different color spaced have different capabilities, pros and cons. You may have to experiment a bit (or look at the example programs) to find out what gives the desired result.

§3. Encoding

When the desired processing is done, it’s time to encode the colors back into some image format. The same rules applies as for the decoding, but the process reversed.

Re-exports§

Modules§

  • Types related to transparent colors.
  • Traits for working with angular values, such as for in hues.
  • Color blending and blending equations.
  • Traits for abstracting over Boolean types.
  • Types for the CIE CAM16 color appearance model.
  • Traits and functions for casting colors to and from other data types.
  • Convert colors from one reference white point to another
  • Algorithms for calculating the difference between colors.
  • Traits related to traditional color theory.
  • Traits for converting between color spaces.
  • Number and color encoding traits, types and standards.
  • Types for the HSL color space.
  • Types for the HSLuv color space.
  • Types for the HSV color space.
  • Hues and hue related types.
  • Types for the HWB color space.
  • Types for the CIE L*a*b* (CIELAB) color space.
  • Types for the CIE L*C*h° color space.
  • Types for the CIE L*C*uv h°uv color space.
  • Types for luma and luminance (grayscale) values.
  • Types for the CIE L*u*v* (CIELUV) color space.
  • A collection of named color constants. Can be toggled with the "named" and "named_from_str" Cargo features.
  • Traits for abstracting over numeric types.
  • Types for the Okhsl color space.
  • Types for the Okhsv color space.
  • Types for the Okhwb color space.
  • Types for the Oklab color space.
  • Types for the Oklch color space.
  • Types for the RGB color space, including spaces and standards.
  • Utilities for serializing and deserializing with serde.
  • Traits for working with stimulus colors and values, such as RGB and XYZ.
  • Defines the tristimulus values of the CIE Illuminants.
  • Types for the CIE 1931 XYZ color space.
  • Types for the CIE 1931 Yxy (xyY) color space.

Structs§

  • An alpha component wrapper for colors, for adding transparency.
  • HSL color space.
  • HSLuv color space.
  • HSV color space.
  • HWB color space.
  • The CIE L*a*b* (CIELAB) color space.
  • A hue type for the CIE L*a*b* family of color spaces.
  • CIE L*C*h°, a polar version of CIE L*a*b*.
  • CIE L*C*uv h°uv, a polar version of CIE L*u*v*.
  • The CIE L*u*v* (CIELUV) color space.
  • A hue type for the CIE L*u*v* family of color spaces.
  • A Hue/Saturation/Lightness representation of Oklab in the sRGB color space.
  • A Hue/Saturation/Value representation of Oklab in the sRGB color space.
  • A Hue/Whiteness/Blackness representation of Oklab in the sRGB color space, similar to Hwb.
  • A hue type for the Oklab color space.
  • Oklch, a polar version of Oklab.
  • A hue type for the RGB family of color spaces.
  • The CIE 1931 XYZ color space.
  • The CIE 1931 Yxy (xyY) color space.

Traits§

  • Extension trait for fixed size arrays.
  • An operator for restricting a color’s components to their expected ranges.
  • An assigning operator for restricting a color’s components to their expected ranges.
  • Operators for darkening a color;
  • Assigning operators for darkening a color;
  • Operator for decreasing the saturation (or chroma) of a color.
  • Assigning operator for decreasing the saturation (or chroma) of a color.
  • A trait for colors where a hue may be calculated.
  • Checks if color components are within their expected range bounds.
  • Operators for lightening a color.
  • Assigning operators for lightening a color.
  • Linear color interpolation of two colors.
  • Assigning linear color interpolation of two colors.
  • Temporary helper trait for getting an array type of size N + 1.
  • A trait for calculating relative contrast between two colors.
  • Operator for increasing the saturation (or chroma) of a color.
  • Assigning operator for increasing the saturation (or chroma) of a color.
  • Change the hue of a color to a specific value without moving.
  • Operator for increasing or decreasing the hue by an amount.
  • Assigning operator for increasing or decreasing the hue by an amount.
  • A trait for color types that can have or be given transparency (alpha channel).
  • Change the hue of a color to a specific value.

Functions§

Type Aliases§

Derive Macros§